#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
/*for getting file size using stat()*/
#include <sys/stat.h>

/*for sendfile()*/
#include <sys/sendfile.h>

/*for O_RDONLY*/
#include <fcntl.h>

// 认证函数，检查用户名和密码是否在 user.csv 中
int authenticate(const char *username, const char *password) {
    FILE *file = fopen("./user.csv", "r");
    if (file == NULL) {
        perror("Failed to open user.csv");
        return 0;
    }

    char line[100];
    // 跳过标题行
    fgets(line, sizeof(line), file);

    while (fgets(line, sizeof(line), file) != NULL) {
        char stored_username[20], stored_password[20];
        if (sscanf(line, "%19[^,],%19s", stored_username, stored_password) == 2) {
            if (strcmp(username, stored_username) == 0 && strcmp(password, stored_password) == 0) {
                fclose(file);
                return 1;
            }
        }
    }

    fclose(file);
    return 0;
}

// 被动模式下监听的套接字
int active_sock;
// 被动模式下的端口号
int active_port;

// 处理被动模式请求
void handle_pasv(int sock2) {
    struct sockaddr_in active_addr;
    // 创建一个新的套接字用于被动模式
    active_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (active_sock == -1) {
        perror("active socket creation failed");
        return;
    }

    bzero(&active_addr, sizeof(active_addr));
    active_addr.sin_family = AF_INET;
    active_addr.sin_addr.s_addr = INADDR_ANY;
    // 随机选择一个端口
    active_port = 30000 + (rand() % 10000);
    active_addr.sin_port = htons(active_port);

    // 绑定端口
    if (bind(active_sock, (struct sockaddr *)&active_addr, sizeof(active_addr)) == -1) {
        perror("active bind failed");
        close(active_sock);
        return;
    }

    // 开始监听
    if (listen(active_sock, 1) == -1) {
        perror("active listen failed");
        close(active_sock);
        return;
    }

    // 将被动模式的端口信息发送给客户端
    char response[100];
    struct sockaddr_in server_addr;
    socklen_t addr_len = sizeof(server_addr);
    getsockname(sock2, (struct sockaddr *)&server_addr, &addr_len);
    char *ip = inet_ntoa(server_addr.sin_addr);
    int port_high = active_port / 256;
    int port_low = active_port % 256;
    sprintf(response, "227 Entering active Mode (%s,%d,%d).", ip, port_high, port_low);
    send(sock2, response, strlen(response) + 1, 0);
}

// 处理客户端请求的线程函数
void *handle_client(void *arg) {
    int sock2 = *(int *)arg;
    struct stat obj;
    char ipaddr[15], buf[100], command[5], filename[200];
    int k, size, port;
    int filehandle;
    char conn_mode[10];  // 存储客户端连接模式
    int client_port = 0; // 存储客户端被动模式下开放的端口
    char *f; // 声明 f 变量

    // 接收客户端的认证消息
    char auth_msg[100];
    recv(sock2, auth_msg, sizeof(auth_msg), 0);

    // 解析用户名、密码、连接模式和客户端端口
    char username[20], password[20];
    if (sscanf(auth_msg, "auth %s %s %s %d", username, password, conn_mode, &client_port) == 4) {
        // 被动模式
        printf("Mode:%s Client port: %d\n", conn_mode, client_port);
    } else if (sscanf(auth_msg, "auth %s %s %s", username, password, conn_mode) == 3) {
        // 主动模式
        printf("Mode:%s \n", conn_mode);
    } else {
        printf("Failed to parse authentication message\n");
        close(sock2);
        pthread_exit(NULL);
    }

    // 调用认证函数
    int auth_status = 0;
    if (authenticate(username, password)) {
        auth_status = 1;
    }

    // 发送认证结果给客户端
    send(sock2, &auth_status, sizeof(int), 0);
    if (!auth_status) {
        printf("Client authentication failed.\n");
        close(sock2); // 关闭客户端连接套接字
        pthread_exit(NULL);
    }
    printf("Client authenticated successfully.\n");

    int i = 1, c;
    // 进入无限循环，持续处理客户端请求
    while (1) {
        // 从客户端接收数据，存储到 buf 中
        recv(sock2, buf, 100, 0);
        // 从 buf 中提取客户端发送的命令，存储到 command 中
        sscanf(buf, "%s", command);
        // 检查客户端发送的命令是否为 "dir"
        if (!strcmp(command, "dir")) {
            // 执行系统命令，将目录列表输出到 temps.txt 文件中
            system("dir >temps.txt");
            // 重置计数器 i
            i = 0;
            // 获取 temps.txt 文件的信息，存储到 obj 中
            stat("temps.txt", &obj);
            // 获取 temps.txt 文件的大小
            size = obj.st_size;
            // 将文件大小发送给客户端
            send(sock2, &size, sizeof(int), 0);
            // 以只读模式打开 temps.txt 文件
            filehandle = open("temps.txt", O_RDONLY);
            // 将文件内容发送给客户端
            sendfile(sock2, filehandle, NULL, size);
        }
        // 检查客户端发送的命令是否为 "get"
        else if (!strcmp(command, "get")) {
            strcpy(filename, buf + strlen(command) + 1);
            char *token = strtok(filename, " ");
            while (token != NULL) {
                // 获取指定文件的信息，存储到 obj 中
                stat(token, &obj);
                // 以只读模式打开指定文件
                filehandle = open(token, O_RDONLY);
                // 获取文件大小
                size = obj.st_size;
                // 若文件打开失败，将文件大小设为 0
                if (filehandle == -1)
                    size = 0;
                // 将文件大小发送给客户端
                send(sock2, &size, sizeof(int), 0);
                // 若文件大小不为 0，将文件内容发送给客户端
                if (size)
                    sendfile(sock2, filehandle, NULL, size);
                close(filehandle);
                token = strtok(NULL, " ");
            }
        }
        // 检查客户端发送的命令是否为 "put"
        else if (!strcmp(command, "put")) {
            char *token = strtok(buf + strlen(command), " ");
            while (token != NULL) {
                // 初始化计数器 c
                int c = 0;
                // 定义用于存储文件内容的缓冲区指针
                char *f;
                // 从 buf 中提取文件名，存储到 filename 中
                strcpy(filename, token);
                // 从客户端接收文件大小
                recv(sock2, &size, sizeof(int), 0);
                // 初始化计数器 i
                i = 1;
                // 循环尝试创建文件，避免文件名冲突
                while (1) {
                    // 若文件不存在则创建，若存在则创建失败
                    // 0666 表示文件权限为读写权限
                    filehandle = open(filename, O_CREAT | O_EXCL | O_WRONLY, 0666);

                    // 若文件创建失败
                    if (filehandle == -1) {
                        // 在文件名后添加序号，避免冲突
                        sprintf(filename + strlen(filename), "%d", i);
                    } else
                        // 若文件创建成功，跳出循环
                        break;
                }

                // 为文件内容分配内存
                f = malloc(size);
                // 从客户端接收文件内容
                recv(sock2, f, size, 0);
                // 将文件内容写入文件
                c = write(filehandle, f, size);
                // 关闭文件
                close(filehandle);
                // 将写入状态发送给客户端
                send(sock2, &c, sizeof(int), 0);
                token = strtok(NULL, " ");
            }
        }
        // 检查客户端发送的命令是否为 "pwd"
        else if (!strcmp(command, "pwd")) {
            // 执行系统命令，将当前工作目录输出到 temp.txt 文件中
            system("pwd >temp.txt");
            // 重置计数器 i
            i = 0;
            // 以只读模式打开 temp.txt 文件
            FILE *f = fopen("temp.txt", "r");
            // 逐字符读取文件内容，存储到 buf 中
            while (!feof(f))
                buf[i++] = fgetc(f);
            // 在 buf 末尾添加字符串结束符
            buf[i - 1] = '\0';
            // 关闭文件
            fclose(f);
            // 将当前工作目录信息发送给客户端
            send(sock2, buf, 100, 0);
        }
        // 检查客户端发送的命令是否为 "cd"
        else if (!strcmp(command, "cd")) {
            // 尝试切换工作目录
            if (chdir(buf + 3) == 0)
                // 若切换成功，设置状态标志 c 为 1
                c = 1;
            else
                // 若切换失败，设置状态标志 c 为 0
                c = 0;
            // 将切换状态发送给客户端
            send(sock2, &c, sizeof(int), 0);
        }
        // 检查客户端发送的命令是否为 "mkdir"
        else if (!strcmp(command, "mkdir")) {
            char *dirname = buf + strlen(command) + 1;
            if (mkdir(dirname, 0777) == 0) {
                c = 1; // 创建成功
            } else {
                c = 0; // 创建失败
            }
            send(sock2, &c, sizeof(int), 0);
        }
        // 检查客户端发送的命令是否为 "bye" 或 "quit"
        else if (!strcmp(command, "bye") || !strcmp(command, "quit")) {
            // 输出服务器退出信息
            printf("FTP server quitting..\n");
            // 设置状态标志 i 为 1
            i = 1;
            // 将状态标志发送给客户端
            send(sock2, &i, sizeof(int), 0);
            // 关闭客户端连接
            close(sock2);
            pthread_exit(NULL);
        }
    }
}

int main() {
    struct sockaddr_in server, client;
    int sock1, sock2;
    int port;
    char ipaddr[15];
    socklen_t len;

    // 创建一个 IPv4 的 TCP 套接字
    sock1 = socket(AF_INET, SOCK_STREAM, 0);

    // 检查套接字创建是否失败
    if (sock1 == -1) {
        // 若失败，输出错误信息并退出程序
        printf("Socket creation failed\n");
        exit(1);
    }

    // 将服务器套接字地址结构体清零
    bzero(&server, sizeof(server));
    // 提示用户输入服务器监听的端口号
    printf("Enter the port number: ");
    // 读取用户输入的端口号
    scanf("%d", &port);
    // 提示用户输入服务器监听的 IP 地址
    printf("\nEnter the ip address in which server will listen request: ");
    // 读取用户输入的 IP 地址
    scanf("%s", ipaddr);

    // 设置服务器套接字地址结构体的地址族为 IPv4
    server.sin_family = AF_INET;
    // 将端口号从主机字节序转换为网络字节序
    server.sin_port = htons(port);
    // 将点分十进制的 IP 地址转换为网络字节序的二进制形式
    server.sin_addr.s_addr = inet_addr(ipaddr);

    // 将监听套接字绑定到指定的 IP 地址和端口号
    if (bind(sock1, (struct sockaddr *)&server, sizeof(server)) == -1) {
        // 若失败，输出错误信息并退出程序
        printf("\nBinding error\n");
        exit(1);
    }
    // 开始监听客户端连接请求，最大连接队列长度为 1
    if (listen(sock1, 1) == -1) {
        // 若失败，输出错误信息并退出程序
        printf("\nListen failed\n");
        exit(1);
    }

    // 获取客户端地址结构体的长度
    len = sizeof(client);

    while (1) {
        // 接受客户端的连接请求，返回一个新的套接字用于与客户端通信
        sock2 = accept(sock1, (struct sockaddr *)&client, &len);
        if (sock2 < 0) {
            printf("\nAccept Failed!..\n");
            continue;
        } else {
            printf("\nRequest Accepted..\n");
        }

        // 创建一个新线程来处理客户端请求
        pthread_t thread_id;
        if (pthread_create(&thread_id, NULL, handle_client, &sock2) != 0) {
            perror("Thread creation failed");
            close(sock2);
        }
        // 分离线程，使其在结束时自动释放资源
        pthread_detach(thread_id);
    }

    // 关闭监听套接字
    close(sock1);
    return 0;
}